package com.ym.soso.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.thread.GlobalThreadPool;
import cn.hutool.extra.spring.SpringUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ym.common.constant.Constants;
import com.ym.common.utils.StringUtil;
import com.ym.common.utils.page.PageDomain;
import com.ym.core.es.domain.bo.GlobalSearchParam;
import com.ym.core.es.domain.bo.GlobalSearchResult;
import com.ym.core.es.domain.index.GlobalSearchWordIndex;
import com.ym.core.es.query.QueryParser;
import com.ym.core.es.service.GlobalSearchWordIndexService;
import com.ym.core.redis.RedisKeyEnum;
import com.ym.core.redis.RedisUtil;
import com.ym.soso.domain.param.AppHealthPlateQueryParam;
import com.ym.soso.domain.param.PensionInfoAppQryParam;
import com.ym.soso.domain.param.SveNoseHouseSimQueryParam;
import com.ym.soso.domain.vo.AppHealthPlateListMobileVO;
import com.ym.soso.domain.vo.PensionInfoSimVO;
import com.ym.soso.domain.vo.SveNoseHouseSimVO;
import com.ym.soso.enums.PublishStatusEnum;
import com.ym.soso.service.IAppHealthPlateSV;
import com.ym.soso.service.IPensionInfoSV;
import com.ym.soso.service.ISearchSV;
import com.ym.soso.service.ISveNoseHouseSV;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 全局搜索
 *
 * @author: 李涛
 * @version: 2020年02月17日 14:12
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class SearchSVImpl implements ISearchSV {


	@Autowired
	private GlobalSearchWordIndexService heightWordService;

	@Autowired
	private IAppHealthPlateSV appHealthPlateSV;

	@Autowired
	private RedisUtil redisUtil;

	@Autowired
	private ISveNoseHouseSV sveNoseHouseSV;

	@Autowired
	private IPensionInfoSV pensionInfoSV;

	@Override
	public List<GlobalSearchWordIndex> globalSearchPrev(String prov, String city, String area, String keyword) throws Exception {
		if (StringUtil.isBlank(keyword)) {
			// 输入字符无效返回空集合
			return Collections.emptyList();
		}
		// 查询条件构建
		QueryParser queryParser = new QueryParser(GlobalSearchWordIndex.class);
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		if (StringUtil.isNotBlank(prov)) {
			queryBuilder.filter(QueryBuilders.termQuery("prov", prov));
		}
		if (StringUtil.isNotBlank(city)) {
			queryBuilder.filter(QueryBuilders.termQuery("city", city));
		}
		if (StringUtil.isNotBlank(area)) {
			queryBuilder.filter(QueryBuilders.termQuery("area", area));
		}
		queryBuilder.filter(QueryBuilders.termQuery("publishStatus", PublishStatusEnum.HAS_BEEN_RELEASED.getKey()));
		MultiMatchQueryBuilder matchQuery = QueryBuilders.multiMatchQuery(keyword, "fieldValue");
		matchQuery.minimumShouldMatch("25%");
		queryBuilder.must(matchQuery);
		queryParser.query(queryBuilder, 10);
		List<GlobalSearchWordIndex> fieldValue = heightWordService.queryForList(queryParser);
		return fieldValue.stream().distinct().collect(Collectors.toList());
	}

	/**
	 * 排名规则，利用redis有序set集合
	 * 1.默认参与排名关键词100个
	 * 2.关键词满100后，移除排名靠后的后50个
	 * 3.热门排行，取固定前5个
	 *
	 * @param keyword
	 */
	@Override
	public void refreshHotSearchWords(String keyword) {
		GlobalThreadPool.execute(() -> {
			// 参与排名缓存数量
			int cacheSize = 100;
			// 淘汰开始位
			int eliminateIndex = cacheSize / 2 + 1;
			RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
			int initScore = 1;
			// 1.检查是否有set集合Key，
			String hotKey = RedisKeyEnum.HOT_SEARCH_KEYWORD.build();
			boolean hasKey = redisUtil.zHasKey(hotKey, keyword);
			// 2.给当前关键词参与排名
			if (!hasKey) {
				// 不存在key，添加初始分数
				redisUtil.zSet(hotKey, keyword, initScore);
			} else {
				// 存在,查询关键词是否满100，满->移除51~100
				long hotSize = redisUtil.zSize(hotKey);
				if (hotSize >= cacheSize) {
					redisUtil.zRemoveByRank(hotKey, 0, eliminateIndex);
				}
				// 当前关键词参加排名
				redisUtil.zIncr(hotKey, keyword, initScore);
			}
		});
	}

	@Override
	public Set<String> getHotKeywords(int limit) {
		String hotKey = RedisKeyEnum.HOT_SEARCH_KEYWORD.build();
		Set<String> strings = redisUtil.zRangeReverse(hotKey, 0, limit);
		return strings;
	}

	@Override
	public GlobalSearchResult globalSearch(GlobalSearchParam param) {
		PageDomain pageDomain = PageDomain.getPageDomain();
		// 返回的结果集
		GlobalSearchResult globalSearchResult = new GlobalSearchResult();
		Map<String, CompletableFuture<PageInfo>> futureMap = new HashMap<>();
		// 1.医院数据查询
		String globalDataTypeEnum = param.getDataType();
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.HOSPITAL)) {

		}

		// 2.中药堂
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.CHINESE_MEDICINE_HALL)) {

		}

		// 3.药店
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.PHARMACY)) {

		}

		// 4.门诊
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.OUTPATIENT_DEPARTMENT)) {

		}

		// 5.体检
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.PHYSICAL_EXAMINATION)) {

		}

		// 7.鼻炎馆
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.RHINITIS_MUSEUM)) {
			CompletableFuture<PageInfo> future = CompletableFuture.supplyAsync(() -> {
				SveNoseHouseSimQueryParam queryParam = new SveNoseHouseSimQueryParam();
				BeanUtils.copyProperties(param, queryParam);
				PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize());
				List<SveNoseHouseSimVO> list = sveNoseHouseSV.selectListMobile(queryParam);
				return new PageInfo<>(list);
			});
			futureMap.put(Constants.IndexDataName.RHINITIS_MUSEUM, future);
		}

		// 8.养老
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.PENSION)) {
			CompletableFuture<PageInfo> penFuture = CompletableFuture.supplyAsync(() -> {
				PensionInfoAppQryParam pensionInfoAppQryParam = new PensionInfoAppQryParam();
				BeanUtils.copyProperties(param, pensionInfoAppQryParam);
				PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize());
				List<PensionInfoSimVO> pensionInfoVoList = pensionInfoSV.listPensionInfo(pensionInfoAppQryParam);
				return new PageInfo<>(pensionInfoVoList);
			});
			futureMap.put(Constants.IndexDataName.PENSION, penFuture);
		}

		// 9.健康咨询
		if (globalDataTypeEnum == null || globalDataTypeEnum.equals(Constants.IndexDataName.APP_HEALTH_PLATE)) {
			CompletableFuture<PageInfo> penFuture = CompletableFuture.supplyAsync(() -> {
				AppHealthPlateQueryParam pensionInfoAppQryParam = new AppHealthPlateQueryParam();
				pensionInfoAppQryParam.setTitle(param.getKeyword());
				pensionInfoAppQryParam.setKeywordSort(true);
				PageInfo<AppHealthPlateListMobileVO> pensionInfoVoList = appHealthPlateSV.pageMobile(pageDomain.getPageNum(), pageDomain.getPageSize(), pensionInfoAppQryParam);
				return pensionInfoVoList;
			});
			futureMap.put(Constants.IndexDataName.APP_HEALTH_PLATE, penFuture);
		}

		// 结果汇总
		CompletableFuture.allOf(futureMap.values().toArray(new CompletableFuture[]{})).join();
		futureMap.forEach((key, value) -> {
			try {
				globalSearchResult.put(key, value.get());
			} catch (Exception e) {
				log.error(ExceptionUtil.stacktraceToString(e));
			}
		});
		// 刷新搜索关键词,如果有结果匹配
		if (!globalSearchResult.isEmpty()) {
			this.refreshHotSearchWords(param.getKeyword());
		}

		return globalSearchResult;
	}
}
